50. 数据框检索

本章概要

  • 学习材料:含订单号、日期、金额与地区的长三角候选表。
  • 本章任务:运行 lst-loc-indexinglst-query-method 与布尔筛选,生成同一候选集。
  • 完成后你将得到:loc/query/布尔结果、关键键集合和候选统计。
  • 自我检查:检查不同检索路径的订单键集合一致;重复键或日期类型错误时先检查原因。
  • 拓展练习:把检索合同拓展应用到另一批订单。

数据检索:数据分析的核心操作

数据检索(Data Retrieval)是数据分析中最基础也最关键的操作之一。

在金融领域,典型应用场景包括:

  • 时间序列分析:从日度股价数据中提取特定年份的子样本
  • 截面研究:筛选符合特定财务指标的上市公司
  • 事件研究:定位公告日前后特定窗口期内的数据
  • 组合构建:根据多因子筛选标准选出目标股票

本章学习目标

通过本章学习,你将掌握以下核心技能:

  1. Pandas的索引系统及其设计哲学
  2. 基于标签(loc)和基于位置(iloc)的两套索引体系
  3. 布尔索引的高级应用技巧
  4. 复杂数据筛选问题的解决思路
  5. 数据检索的性能优化策略

数据检索的数学基础

一个二维数据框可以表示为矩阵 \(D \in \mathbb{R}^{n \times m}\)

  • 行选择:给定行索引集合 \(I\),提取子矩阵 \(D_{I,:}\)
  • 列选择:给定列索引集合 \(J\),提取子矩阵 \(D_{:,J}\)
  • 条件筛选:给定谓词函数 \(P\),提取满足条件的行

\[ \large I_P=\{i\in\{1,\ldots,n\}\mid P(d_{i,:})=\text{True}\},\qquad D_{\text{filtered}}=D_{I_P,:} \]

Pandas的两套索引系统

Pandas提供了两套并行的索引系统:

特性 loc(标签索引) iloc(位置索引)
索引类型 数据标签(代码、日期等) 整数位置(从0开始)
切片行为 闭区间 [start, end] 半开区间 [start, end)
适用场景 语义查询(找特定股票) 算法实现(取前N行)
  • 遵循了 Python 哲学:显式优于隐性

列数据的三种访问方式

数据声明:以下四行价格、涨跌幅和成交量均为匿名课堂构造,不对应任何真实公司或具体交易日。

Listing 1: DataFrame列的三种访问方式
展开列访问代码
import pandas as pd
import numpy as np

# 创建匿名课堂行情示例
data = {  # 定义四项不映射真实公司的课堂记录
    '股票代码': ['SIM-A', 'SIM-B', 'SIM-C', 'SIM-D'],  # 设置匿名模拟代码
    '股票名称': ['模拟白酒A', '模拟白酒B', '模拟银行A', '模拟保险A'],  # 设置匿名模拟名称并保留行业词示例
    '收盘价': [1850.0, 220.5, 45.2, 52.8],  # 设置课堂模拟收盘价
    '涨跌幅': [0.05, -0.02, 0.03, -0.01],  # 设置课堂模拟涨跌幅
    '成交量': [1200, 3500, 8900, 5600]  # 设置课堂模拟成交量
}
df = pd.DataFrame(data)

# 方法1:点号访问(列名必须是合法Python标识符)
names = df.股票名称
print('方法1 - 点号选择:')
print(f'类型: {type(names)}')
print(names)
方法1 - 点号选择:
类型: <class 'pandas.core.series.Series'>
0    模拟白酒A
1    模拟白酒B
2    模拟银行A
3    模拟保险A
Name: 股票名称, dtype: object

方括号访问与链式操作

Listing 2: 使用方括号访问列
展开方括号访问代码
# 方法2:方括号访问(最通用,推荐)
prices = df['收盘价']
print('方法2 - 方括号选择:')
print(prices)
方法2 - 方括号选择:
0    1850.0
1     220.5
2      45.2
3      52.8
Name: 收盘价, dtype: float64
Listing 3: 链式操作示例
展开链式操作代码
# 方法3:链式操作
mean_price = df.收盘价.mean()
print(f'\n平均价格: {mean_price:.2f}元')

# 验证:方括号方式结果相同
mean_price_alt = df['收盘价'].mean()
print(f'验证(应相同): {mean_price_alt:.2f}元')

平均价格: 542.12元
验证(应相同): 542.12元

多列选择

Listing 4: 多列选择方法
展开多列选择代码
# 使用列表选择多列(注意双重方括号)
subset = df[['股票代码', '股票名称', '收盘价']]
print('多列选择结果:')
print(subset)
print(f'\n数据类型: {type(subset)}')
多列选择结果:
    股票代码   股票名称     收盘价
0  SIM-A  模拟白酒A  1850.0
1  SIM-B  模拟白酒B   220.5
2  SIM-C  模拟银行A    45.2
3  SIM-D  模拟保险A    52.8

数据类型: <class 'pandas.core.frame.DataFrame'>
  • 单列选择返回 Series(一维数据)
  • 多列选择返回 DataFrame(二维表格)
  • 适用于从宽表中提取关键指标

使用loc进行标签索引

Listing 5: 使用loc进行基于标签的索引
# 设置股票代码为索引
df_indexed = df.set_index('股票代码')
print('索引后的DataFrame:')
print(df_indexed)
print('\n行索引:', df_indexed.index.tolist())
索引后的DataFrame:
        股票名称     收盘价   涨跌幅   成交量
股票代码                            
SIM-A  模拟白酒A  1850.0  0.05  1200
SIM-B  模拟白酒B   220.5 -0.02  3500
SIM-C  模拟银行A    45.2  0.03  8900
SIM-D  模拟保险A    52.8 -0.01  5600

行索引: ['SIM-A', 'SIM-B', 'SIM-C', 'SIM-D']

loc选择单行与多行

展开单行选择代码
# 选择单行:返回Series
stock1 = df_indexed.loc['SIM-A']  # 按匿名课堂代码选择单行
print('选择单行 - 模拟白酒A:')  # 标明输出来自匿名课堂样本
print(stock1)
print(f'返回类型: {type(stock1)}')
选择单行 - 模拟白酒A:
股票名称     模拟白酒A
收盘价     1850.0
涨跌幅       0.05
成交量       1200
Name: SIM-A, dtype: object
返回类型: <class 'pandas.core.series.Series'>

loc 选择多行

展开多行选择代码
# 选择多行:返回DataFrame
stocks_subset = df_indexed.loc[['SIM-A', 'SIM-B']]  # 按两个匿名课堂代码选择多行
print('\n选择多行:')
print(stocks_subset[['股票名称', '收盘价']])
print(f'返回类型: {type(stocks_subset)}')

选择多行:
        股票名称     收盘价
股票代码                
SIM-A  模拟白酒A  1850.0
SIM-B  模拟白酒B   220.5
返回类型: <class 'pandas.core.frame.DataFrame'>

loc切片:闭区间特性

Listing 6: loc切片包含结束位置
# loc切片包含结束位置(闭区间 [start, end])
stocks_slice = df_indexed.loc['SIM-A':'SIM-C']  # 演示标签闭区间包含SIM-C
print('loc切片操作(包含边界):')
print(stocks_slice)
loc切片操作(包含边界):
        股票名称     收盘价   涨跌幅   成交量
股票代码                            
SIM-A  模拟白酒A  1850.0  0.05  1200
SIM-B  模拟白酒B   220.5 -0.02  3500
SIM-C  模拟银行A    45.2  0.03  8900
  • loc切片包含结束位置(闭区间)
  • 符合直觉:说”从A到B”通常包含A和B
  • 与Python传统切片(半开区间)不同

同时选择行和列

Listing 7: 同时选择行和列
展开行列组合选择代码
# 行标签列表 + 列名列表
df_subset = df_indexed.loc[['SIM-A', 'SIM-B'], ['股票名称', '收盘价']]  # 组合选择匿名课堂样本的行列
print('行和列的组合选择:')
print(df_subset)

# 选择单个标量值
specific_value = df_indexed.loc['SIM-A', '收盘价']  # 读取匿名课堂样本的单个标量
print(f'\n单个标量值: {specific_value}')
print(f'类型: {type(specific_value)}')
行和列的组合选择:
        股票名称     收盘价
股票代码                
SIM-A  模拟白酒A  1850.0
SIM-B  模拟白酒B   220.5

单个标量值: 1850.0
类型: <class 'numpy.float64'>
  • df.at[row_label, col_label] 是专门为标量访问优化的方法,比loc更快

使用iloc进行位置索引

Listing 8: 使用iloc进行基于位置的索引
df_reset = df.reset_index(drop=True)

# 选择第0行(第一行)
row_0 = df_reset.iloc[0]
print('iloc选择第0行:')
print(row_0)
iloc选择第0行:
股票代码     SIM-A
股票名称     模拟白酒A
收盘价     1850.0
涨跌幅       0.05
成交量       1200
Name: 0, dtype: object

iloc切片与负索引

展开 iloc 切片代码
# iloc切片:半开区间 [start, end)
rows_1_2 = df_reset.iloc[1:3]
print('iloc切片1:3(不包含索引3):')
print(rows_1_2)

# 选择特定行和列的位置
cell = df_reset.iloc[0, 2]
print(f'\n第0行第2列的值: {cell}')

# 负索引:从末尾计数
last_row = df_reset.iloc[-1]
print(f'\n最后一行:\n{last_row}')
iloc切片1:3(不包含索引3):
    股票代码   股票名称    收盘价   涨跌幅   成交量
1  SIM-B  模拟白酒B  220.5 -0.02  3500
2  SIM-C  模拟银行A   45.2  0.03  8900

第0行第2列的值: 1850.0

最后一行:
股票代码    SIM-D
股票名称    模拟保险A
收盘价      52.8
涨跌幅     -0.01
成交量      5600
Name: 3, dtype: object
  • iloc的切片不包含结束位置(半开区间),与Python列表一致

使用iloc遍历行

Listing 9: 使用iloc进行行遍历
print('使用iloc遍历每一行:')
for i in range(len(df_reset)):
    row = df_reset.iloc[i]
    if i < 3:
        print(f'第{i}行: {row["股票名称"]} 收盘价={row["收盘价"]}')
使用iloc遍历每一行:
第0行: 模拟白酒A 收盘价=1850.0
第1行: 模拟白酒B 收盘价=220.5
第2行: 模拟银行A 收盘价=45.2
  • 大规模数据处理时,尽量避免循环
  • 优先使用向量化操作(性能差距可达百倍)

布尔索引:单条件筛选

Listing 10: 单条件筛选示例
展开实现代码
# 构建布尔条件:返回True/False的Series
condition = df['收盘价'] > 100
print('布尔条件(收盘价>100):')
print(condition)

# 应用布尔条件筛选
high_price = df[condition]
print('\n筛选结果(收盘价>100的股票):')
print(high_price)
布尔条件(收盘价>100):
0     True
1     True
2    False
3    False
Name: 收盘价, dtype: bool

筛选结果(收盘价>100的股票):
    股票代码   股票名称     收盘价   涨跌幅   成交量
0  SIM-A  模拟白酒A  1850.0  0.05  1200
1  SIM-B  模拟白酒B   220.5 -0.02  3500

多条件筛选:AND 与 OR

Listing 11: 多条件筛选(AND和OR)
展开实现代码
# AND条件:价格>50 且 涨幅>0(必须用括号!)
condition_and = (df['收盘价'] > 50) & (df['涨跌幅'] > 0)
print('AND条件筛选(价格>50 且 涨幅>0):')
result_and = df[condition_and]
print(result_and[['股票名称', '收盘价', '涨跌幅']])

# OR条件:名称含"银行"或"保险"
condition_or = (df['股票名称'].str.contains('银行')) | (df['股票名称'].str.contains('保险'))
print('\n\nOR条件筛选(银行或保险):')
result_or = df[condition_or]
print(result_or[['股票名称', '收盘价']])
AND条件筛选(价格>50 且 涨幅>0):
    股票名称     收盘价   涨跌幅
0  模拟白酒A  1850.0  0.05


OR条件筛选(银行或保险):
    股票名称   收盘价
2  模拟银行A  45.2
3  模拟保险A  52.8

多条件筛选的运算符

运算符 含义 示例
& AND(同时满足) (df['PE'] < 20) & (df['ROE'] > 0.15)
\| OR(满足其一) (df['行业'] == '银行') \| (df['行业'] == '保险')
~ NOT(条件取反) ~df['代码'].isin(blacklist)
  • 重要:每个条件必须用括号包围
  • 位运算符优先级高于比较运算符

query方法:简化复杂条件

Listing 12: 使用query方法简化复杂条件
展开 query 对照代码
# 传统布尔索引方法
complex_condition = (
    (df['收盘价'] > 40) &
    (df['收盘价'] < 2000) &
    (df['涨跌幅'] > 0) &
    (df['成交量'] > 2000)
)
result_complex = df[complex_condition]
print('复杂条件筛选结果:')
print(result_complex[['股票名称', '收盘价', '涨跌幅', '成交量']])

# query方法:语法更简洁
result_query = df.query('收盘价 > 40 and 收盘价 < 2000 and 涨跌幅 > 0 and 成交量 > 2000')
print('\n使用query方法的等价结果:')
print(result_query[['股票名称', '收盘价', '涨跌幅', '成交量']])
复杂条件筛选结果:
    股票名称   收盘价   涨跌幅   成交量
2  模拟银行A  45.2  0.03  8900

使用query方法的等价结果:
    股票名称   收盘价   涨跌幅   成交量
2  模拟银行A  45.2  0.03  8900

isin方法:成员资格检查

Listing 13: 使用isin进行成员资格检查
展开成员筛选代码
# 从股票池中筛选关注列表中的股票
watchlist = ['SIM-A', 'SIM-C']  # 设置匿名课堂关注列表
selected = df[df['股票代码'].isin(watchlist)]
print('筛选关注列表中的股票:')
print(selected[['股票代码', '股票名称', '收盘价']])

# 组合使用:关注列表中且上涨的股票
selected_and_rising = df[
    df['股票代码'].isin(watchlist) &
    (df['涨跌幅'] > 0)
]
print('\n关注列表中上涨的股票:')
print(selected_and_rising[['股票名称', '涨跌幅']])
筛选关注列表中的股票:
    股票代码   股票名称     收盘价
0  SIM-A  模拟白酒A  1850.0
2  SIM-C  模拟银行A    45.2

关注列表中上涨的股票:
    股票名称   涨跌幅
0  模拟白酒A  0.05
2  模拟银行A  0.03
  • isin() 的哈希成员探测通常是均摊 \(O(1)\) / 元素,但整列检查仍约为 \(O(n+k)\)\(n\) 为行数,\(k\) 为候选项数);是否更快需实测。

反向选择:~ 运算符

Listing 14: 反向筛选示例
# 排除特定股票(黑名单筛选)
excluded = df[~df['股票代码'].isin(['SIM-B', 'SIM-D'])]  # 排除两个匿名课堂代码
print('排除特定股票后的结果:')
print(excluded[['股票代码', '股票名称']])
排除特定股票后的结果:
    股票代码   股票名称
0  SIM-A  模拟白酒A
2  SIM-C  模拟银行A
  • 常用于剔除ST股票、风险股票
  • ~ 可与其他条件自由组合

索引的设置与重置

Listing 15: 索引的设置与重置
展开索引变换代码
# 设置索引
df_with_index = df.set_index('股票代码')

# 重置索引(索引变回普通列)
df_reset = df_with_index.reset_index()

# 丢弃原索引的重置
df_reset_drop = df_with_index.reset_index(drop=True)
index_check = pd.Series({'设置后索引名称': df_with_index.index.name, '普通重置首列': df_reset.columns[0], '丢弃重置含股票代码': '股票代码' in df_reset_drop.columns, '三种结果行数一致': len(df_with_index) == len(df_reset) == len(df_reset_drop)})  # 汇总三种索引变换的便于核对结果
print('索引设置与重置核验:')  # 标明输出对应索引结构变化
print(index_check)  # 输出字段保留与行数一致性依据
索引设置与重置核验:
设置后索引名称       股票代码
普通重置首列        股票代码
丢弃重置含股票代码    False
三种结果行数一致      True
dtype: object

性能优化策略一:重复查询与索引

Listing 16: 索引对查找性能的影响
展开索引性能代码
import time

np.random.seed(42)  # 固定随机种子使每次课堂性能示例一致
n_rows = 100000  # 设置匿名课堂数据规模
large_df = pd.DataFrame({'code': np.random.choice([f'SIM-{i:04d}' for i in range(1, 1001)], n_rows), 'price': np.random.uniform(10, 100, n_rows), 'volume': np.random.randint(1000, 10000, n_rows)})  # 构造十万行匿名课堂数据
query_codes = [f'SIM-{i:04d}' for i in range(451, 551)]  # 设置一百个重复查询键
repeat_count = 5  # 设置每个查询键重复五轮
indexed_df = large_df.set_index('code')  # 预先构建索引以评估重复查询阶段
start = time.perf_counter()  # 记录布尔扫描计时起点
scan_results = [large_df[large_df['code'] == code] for _ in range(repeat_count) for code in query_codes]  # 对相同键执行五百次布尔扫描
scan_time = time.perf_counter() - start  # 计算匹配工作量的布尔扫描耗时
start = time.perf_counter()  # 记录索引查询计时起点
index_results = [indexed_df.loc[[code]] for _ in range(repeat_count) for code in query_codes]  # 对相同键执行五百次索引查询
index_time = time.perf_counter() - start  # 计算匹配工作量的索引查询耗时
workload_count = len(query_codes) * repeat_count  # 计算两种方法共同的查询次数
assert len(scan_results) == len(index_results) == workload_count  # 验证两种方法执行完全相同的查询次数
assert all(len(scan_result) == len(index_result) for scan_result, index_result in zip(scan_results, index_results))  # 验证每次查询返回相同行数
benchmark_summary = pd.Series({'每种方法查询次数': workload_count, '布尔扫描耗时(ms)': scan_time * 1000, '索引查询耗时(ms)': index_time * 1000, '扫描/索引耗时比': scan_time / index_time})  # 汇总匹配工作量的示例计时
print(benchmark_summary.round(2))  # 输出当前运行环境的课堂基准结果
每种方法查询次数       500.00
布尔扫描耗时(ms)    2891.89
索引查询耗时(ms)    1036.70
扫描/索引耗时比         2.79
dtype: float64

限制:计时不含索引构建成本,仅说明同一数据上重复查询阶段的本机示例;单次查询、不同索引类型或不同硬件不保证加速,更不能概括为端到端 \(O(1)\)

性能优化策略二:向量化替代循环

Listing 17: 向量化vs循环的性能对比
展开向量化性能代码
# 不推荐:使用循环
start = time.time()
prices_loop = []
for i in range(len(df)):
    if df.iloc[i]['收盘价'] > 100:
        prices_loop.append(df.iloc[i]['收盘价'])
time_loop = time.time() - start

# 推荐:向量化操作
start = time.time()
prices_vectorized = df.loc[df['收盘价'] > 100, '收盘价']
time_vectorized = time.time() - start

print(f'循环方法耗时: {time_loop*1000:.2f}毫秒')
print(f'向量化方法耗时: {time_vectorized*1000:.2f}毫秒')
循环方法耗时: 0.43毫秒
向量化方法耗时: 0.58毫秒
  • 向量化操作底层用C/Fortran实现,避免Python解释器开销
  • 利用CPU的SIMD并行计算和内存局部性

性能优化小结

优化策略 原理 适用场景
使用.loc而非链式索引 避免不必要的内存复制 所有筛选+取列操作
设置索引加速查找 重复查询可避免每次全表扫描,但有构建与维护成本 同一键空间上的多次查找
向量化替代循环 C/Fortran底层优化 所有批量数据操作
使用.at标量访问 专为单值优化 只需取一个值

综合案例:课堂模拟候选筛选

数据声明:以下 1000 行估值与财务指标均由固定种子生成,仅用于演示检索流程,不是 A 股实证样本或投资建议。

Listing 18: 综合应用:课堂模拟候选筛选
展开课堂筛选数据代码
import pandas as pd
import numpy as np

# 生成1000行匿名课堂候选数据
np.random.seed(42)  # 固定随机种子以保证每次筛选结果一致
n_stocks = 1000  # 设置课堂模拟候选行数
stocks_data = pd.DataFrame({
    '股票代码': [f'SIM-{i:04d}' for i in range(1, n_stocks + 1)],  # 生成不映射真实证券的匿名代码
    '股票名称': [f'股票{i}' for i in range(1, n_stocks+1)],
    '市盈率': np.random.uniform(5, 80, n_stocks),
    '市净率': np.random.uniform(0.5, 10, n_stocks),
    'ROE': np.random.uniform(0.05, 0.35, n_stocks),
    '负债率': np.random.uniform(0.2, 0.9, n_stocks),
    '股息率': np.random.uniform(0.005, 0.08, n_stocks)
})

print(f'课堂模拟候选集: {len(stocks_data)}行')  # 标明输出不是市场股票池
print('\n课堂生成指标概况:')  # 标明描述统计来自生成数据
print(stocks_data[['市盈率', '市净率', 'ROE', '负债率', '股息率']].describe())
课堂模拟候选集: 1000行

课堂生成指标概况:
               市盈率          市净率          ROE          负债率          股息率
count  1000.000000  1000.000000  1000.000000  1000.000000  1000.000000
mean     41.769241     5.316664     0.200722     0.543263     0.042058
std      21.910302     2.775804     0.087202     0.200543     0.021511
min       5.347402     0.530574     0.050003     0.200457     0.005002
25%      22.697995     2.790206     0.128405     0.369331     0.023373
50%      42.260553     5.427972     0.200184     0.538995     0.042095
75%      60.823969     7.724418     0.277731     0.716279     0.060500
max      79.978825     9.994430     0.349346     0.899690     0.079831

生成课堂候选清单

课堂启发式阈值:PE<20、PB<3、ROE>15%、负债率<60%、股息率>2% 仅用于演示布尔检索,不是跨行业、跨时期通用标准。

展开筛选条件代码
# 筛选条件:PE<20, PB<3, ROE>15%, 负债率<60%, 股息率>2%
candidate_criteria = (  # 组合五项课堂启发式筛选条件
    (stocks_data['市盈率'] < 20) &
    (stocks_data['市净率'] < 3) &
    (stocks_data['ROE'] > 0.15) &
    (stocks_data['负债率'] < 0.6) &
    (stocks_data['股息率'] > 0.02)
)
candidate_stocks = stocks_data[candidate_criteria].copy()  # 生成课堂候选清单而非投资组合

# 按ROE降序展示前三项课堂候选
candidate_stocks_sorted = candidate_stocks.sort_values('ROE', ascending=False)  # 按课堂模拟ROE排序候选清单
candidate_count = len(candidate_stocks)  # 计算固定种子下的候选数量
candidate_rate = candidate_count / len(stocks_data) * 100  # 计算候选通过率百分比
assert candidate_count == 13  # 验证固定种子结果为13项候选
assert np.isclose(candidate_rate, 1.3)  # 验证13除以1000对应1.3%的通过率
screen_summary = pd.Series({'候选数量': candidate_count, '通过率(%)': candidate_rate})  # 汇总候选规模与通过率
top_value_stocks = candidate_stocks_sorted[['股票代码', '市盈率', '市净率', 'ROE', '负债率', '股息率']].head(3).round(2)  # 保留ROE最高的三项课堂候选
print('课堂候选筛选摘要:')  # 标明可见输出为候选清单核验
print(screen_summary.round(2))  # 输出筛选数量与通过率
print('\nROE最高的三项课堂候选:')  # 标明排序摘要的范围
print(top_value_stocks)  # 输出Top 3课堂候选的关键指标
课堂候选筛选摘要:
候选数量      13.0
通过率(%)     1.3
dtype: float64

ROE最高的三项课堂候选:
         股票代码    市盈率   市净率   ROE   负债率   股息率
576  SIM-0577  16.03  0.72  0.34  0.34  0.04
100  SIM-0101   7.36  1.05  0.32  0.26  0.05
925  SIM-0926   6.69  1.38  0.32  0.22  0.02

课堂候选清单统计特征

展开候选清单统计代码
print('课堂候选清单统计特征:')  # 标明输出不是已构建投资组合
print(f'平均市盈率: {candidate_stocks["市盈率"].mean():.2f}')  # 输出候选清单平均市盈率
print(f'平均市净率: {candidate_stocks["市净率"].mean():.2f}')  # 输出候选清单平均市净率
print(f'平均ROE: {candidate_stocks["ROE"].mean():.2%}')  # 输出候选清单平均模拟ROE
print(f'平均股息率: {candidate_stocks["股息率"].mean():.2%}')  # 输出候选清单平均模拟股息率
课堂候选清单统计特征:
平均市盈率: 12.71
平均市净率: 1.45
平均ROE: 25.41%
平均股息率: 4.34%
  • 固定种子结果:13/1000 = 1.3%;这只描述课堂生成数据,不能推出真实市场中“价值股稀缺”。
  • 启发式解释:低 PE/PB、高 ROE、较低负债率和较高股息率只是候选线索;不能单独证明估值便宜、竞争优势、偿债安全或未来分红。
  • 决策边界:尚未定义权重、持有期、再平衡、回测、交易成本或风险约束,因此只能称为候选清单,不能称为投资组合或主张策略表现。

运行前预测|平台任务解答代码

  • 输入预测:运行前先写出 price_SHSC 的业务含义、数据类型或取值范围,并判断哪一个输入最可能改变结果。
  • 结果预测:不展开答案,先预测将得到“2024-04-08”相关结果;同时写出方向、数量级或表格/图形结构。
  • 完成要求:能独立说明本任务从输入到“平台任务解答代码”结果的关键步骤,原样录入平台代码并得到可核对的运行结果。

⭐ 平台任务解答代码

展开完整代码(投影默认折叠)
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
#任务一
import pandas as pd
price_SHSC = pd.read_excel("https://huoran.oss-cn-shenzhen.aliyuncs.com/1726305586753.xlsx") #导入数据
price_SHSC["日期"] = pd.to_datetime(price_SHSC["日期"] , format='%Y%m%d')  # 转换为日期时间格式
price_SHSC.set_index("日期",inplace=True)  # 将日期列设为price_SHSC数据框的索引

print(price_SHSC.index) #查看数据框的行索引
print(price_SHSC.columns) #查看数据框的列名
print(price_SHSC.shape)  #看看数据框的形状参数

#任务二
import pandas as pd
price_SHSC = pd.read_excel("https://huoran.oss-cn-shenzhen.aliyuncs.com/1726305586753.xlsx") #导入数据

price_SHSC["日期"] = pd.to_datetime(price_SHSC["日期"] , format='%Y%m%d')  # 转换为日期时间格式
price_SHSC.set_index("日期",inplace=True)  # 将日期列设为price_SHSC数据框的索引

print(price_SHSC.loc["2024-04-08"])   #查看2024年4月8日的收盘价数据
print(price_SHSC.loc["2024-05-16"])   #查看2024年5月16日的收盘价数据
print(price_SHSC.loc["2024-06-11"])   #查看2024年6月11日的收盘价数据
print(price_SHSC.loc["2024-06-18":"2024-06-21"]) #查看2024年6月18日到2024年6月21日的数据

#任务三
import pandas as pd
price_SHSC = pd.read_excel("https://huoran.oss-cn-shenzhen.aliyuncs.com/1726305586753.xlsx") #导入数据
price_SHSC["日期"] = pd.to_datetime(price_SHSC["日期"] , format='%Y%m%d')  # 转换为日期时间格式
price_SHSC.set_index("日期",inplace=True)  # 将日期列设为price_SHSC数据框的索引
print(price_SHSC.iloc[15:28,1:4])   #截取第16行至18行、第二列至第四列

#任务四
import pandas as pd
price_SHSC = pd.read_excel("https://huoran.oss-cn-shenzhen.aliyuncs.com/20220819/xlsx/1560448223221932032.xlsx") #导入数据
price_SHSC["日期"] = pd.to_datetime(price_SHSC["日期"] , format='%Y%m%d')  # 转换为日期时间格式
price_SHSC.set_index("日期",inplace=True)  # 将日期列设为price_SHSC数据框的索引
print(price_SHSC[price_SHSC["白云机场"]>=10])  #截取白云机场收盘价大于等于17.6元对应的子数据框
# 输出华能国际
print(price_SHSC[(price_SHSC["华能国际"]<9.5)&(price_SHSC["南方航空"]>5.5)&(price_SHSC["三一重工"]<16)]) 

任务复盘|平台任务解答代码

运行后核对:核对 price_SHSC 是否按预测参与运算,实际输出是否与预测一致;若不一致,先检查类型、单位、索引/字段和运算顺序。

本地纠错(平台原注释保持不变)iloc[15:28, 1:4] 实际选取位置 15–27,即按自然序号为第 16–28 行,而不是“第16行至18行”;price_SHSC["白云机场"] >= 10 的实际阈值是 10 元,而不是注释中的 17.6 元。

拓展练习:把输入表替换为本地中国上市公司数据的同结构子集;指出必须保持的字段、数据类型和质量检查。

本章小结

操作类型 方法 关键特性
列选择 df['列名'] / df[['列1','列2']] 单列返回Series,多列返回DataFrame
标签索引 df.loc[行标签, 列标签] 闭区间切片,语义清晰
位置索引 df.iloc[行位置, 列位置] 半开区间切片,算法友好
布尔筛选 df[条件] &/\|/~组合,括号必须
成员检查 df['列'].isin(列表) 单次哈希探测通常均摊常数级,整列仍需遍历
简洁查询 df.query('条件表达式') 类SQL语法,复杂条件可读性好

随堂练习

  • 问题 1|需要准备哪些数据?:含订单号、日期、金额与地区的长三角候选表。
  • 问题 2|需要完成哪些操作?:运行 lst-loc-indexinglst-query-method 与布尔筛选,生成同一候选集。
  • 问题 3|应得到哪些结果?:loc/query/布尔结果、关键键集合和候选统计。
  • 问题 4|怎样确认结果可靠?:检查不同检索路径的订单键集合一致;重复键或日期类型错误时先检查原因。
  • 问题 5|换一个情境,怎样继续应用?:把检索合同拓展应用到另一批订单。
  • 作答提示:请依次写清所用数据、分析过程、所得结果、核对方法和拓展思考。课程所需数据见前言中的下载入口;教学平台固定题按页面说明完成。

教师参考解答|答案与说明 1

  • 所用数据与字段code:str, area:str, pe:float, authorized:bool;只允许授权且有限的记录进入候选。

教师参考解答|代码 1

展开代码(代码区可独立滚动)
import pandas as pd, numpy as np  # 导入表格工具以构造、对齐和核对数据
frame=pd.DataFrame({'code':['A','B','C','D'],'area':['上海','浙江','江苏','北京'],'pe':[15,35,np.nan,20],'authorized':[True,True,True,False]})  # 构造带业务字段的数据框输入
mask=frame['area'].isin(['上海','浙江','江苏']) & frame['pe'].between(0,30) & frame['authorized']  # 构造带业务字段的数据框输入
candidate=frame.loc[mask,['code','area','pe']].copy()  # 构造带业务字段的数据框输入
manual=[row.code for row in frame.itertuples() if row.area in {'上海','浙江','江苏'} and pd.notna(row.pe) and 0<=row.pe<=30 and row.authorized]  # 用逐项公式独立复算向量结果
assert candidate['code'].tolist()==manual  # 用逐项公式独立复算向量结果
print(mask.tolist(),candidate)  # 按标签与条件筛出候选记录

教师参考解答|答案与说明 2

  • 解释答案:loc 按标签、iloc 按位置、布尔条件按规则筛行;代码、日期和数值条件组合时必须先固定索引语义并检查结果行数。
  • 拓展应用答案:拓展应用到订单明细时,以订单号和日期替换证券索引,比较 loc、query 与布尔筛选结果,并检查关键键集合一致。
  • 参考结果:布尔轨迹与候选A。另行核对:逐行真值表;报告筛选前后行数与缺失被排除数。
  • 常见错误and 代替 &;缺括号;链式赋值;把哈希单次探测概括成整列 O(1)。